SET(TEST_WITH_REDIS_VERSION "6.2.1" CACHE STRING "Redis version used when running tests.")

# Re-enable `-std=gnu99` for tests only, this avoids the need to sprinkle
# `#define  _XOPEN_SOURCE 600` in test code for e.g. strdup()
set(CMAKE_C_EXTENSIONS ON)

if(ENABLE_SSL)
  # Generate SSL certs and keys when needed
  set(SSL_CONFIGS ca.crt ca.key ca.txt redis.crt redis.key client.crt client.key)
  add_custom_command(
    OUTPUT ${SSL_CONFIGS}
    COMMAND openssl genrsa -out ca.key 4096
    COMMAND openssl req -x509 -new -nodes -sha256 -key ca.key -days 3650 -subj '/CN=Redis Test CA' -out ca.crt
    COMMAND openssl genrsa -out redis.key 2048
    COMMAND openssl req -new -sha256 -key redis.key -subj '/CN=Redis Server Test Cert' | openssl x509 -req -sha256 -CA ca.crt -CAkey ca.key -CAserial ca.txt -CAcreateserial -days 365 -out redis.crt
    COMMAND openssl genrsa -out client.key 2048
    COMMAND openssl req -new -sha256 -key client.key -subj '/CN=Redis Client Test Cert' | openssl x509 -req -sha256 -CA ca.crt -CAkey ca.key -CAserial ca.txt -CAcreateserial -days 365 -out client.crt
    )
  add_custom_target(generate_tls_configs DEPENDS ${SSL_CONFIGS})

  set(SSL_LIBRARY hiredis_cluster_ssl)
endif()

# Targets to setup Redis Clusters for testing
if(ENABLE_IPV6_TESTS)
  set(NO_IPV6 "")
else()
  set(NO_IPV6 "true") # Ignore command
endif()

add_custom_target(start
  COMMAND REDIS_VERSION=${TEST_WITH_REDIS_VERSION} PORT=7000 ${CMAKE_SOURCE_DIR}/tests/scripts/redis-cluster start
  COMMAND REDIS_VERSION=${TEST_WITH_REDIS_VERSION} PORT=7100 ADDITIONAL_OPTIONS='--requirepass secretword --masterauth secretword' ADDITIONAL_CLI_OPTIONS='-a secretword' ${CMAKE_SOURCE_DIR}/tests/scripts/redis-cluster start
  COMMAND ${NO_IPV6} REDIS_VERSION=${TEST_WITH_REDIS_VERSION} PORT=7200 CLUSTER_HOST=::1 ADDITIONAL_OPTIONS='--bind ::1' ADDITIONAL_CLI_OPTIONS='-h ::1' ${CMAKE_SOURCE_DIR}/tests/scripts/redis-cluster start
  )
add_custom_target(stop
  COMMAND PORT=7000 ${CMAKE_SOURCE_DIR}/tests/scripts/redis-cluster stop
  COMMAND PORT=7100 ${CMAKE_SOURCE_DIR}/tests/scripts/redis-cluster stop
  COMMAND ${NO_IPV6} PORT=7200 ${CMAKE_SOURCE_DIR}/tests/scripts/redis-cluster stop
)

# Find dependencies
find_package(PkgConfig REQUIRED)
pkg_check_modules(GLIB_LIBRARY IMPORTED_TARGET glib-2.0)
find_library(LIBUV_LIBRARY uv HINTS /usr/lib/x86_64-linux-gnu)
find_library(LIBEV_LIBRARY ev HINTS /usr/lib/x86_64-linux-gnu)
find_library(LIBEVENT_LIBRARY event HINTS /usr/lib/x86_64-linux-gnu)
find_path(LIBEVENT_INCLUDES event2/event.h)
include_directories(${LIBEVENT_INCLUDES})

if(MSVC OR MINGW)
  find_library(LIBEVENT_LIBRARY Libevent)
else()
  add_compile_options(-Wall -Wextra -pedantic -Werror)
  # Debug mode for tests
  set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "" FORCE)
endif()

# Make sure ctest gives the output when tests fail
list(APPEND CMAKE_CTEST_ARGUMENTS "--output-on-failure")

add_executable(ct_async ct_async.c)
target_link_libraries(ct_async hiredis_cluster ${SSL_LIBRARY} ${LIBEVENT_LIBRARY})
add_test(NAME ct_async COMMAND "$<TARGET_FILE:ct_async>")
set_tests_properties(ct_async PROPERTIES LABELS "CT")

add_executable(ct_commands ct_commands.c test_utils.c)
target_link_libraries(ct_commands hiredis_cluster ${SSL_LIBRARY})
add_test(NAME ct_commands COMMAND "$<TARGET_FILE:ct_commands>")
set_tests_properties(ct_commands PROPERTIES LABELS "CT")

add_executable(ct_connection ct_connection.c test_utils.c)
target_link_libraries(ct_connection hiredis_cluster ${SSL_LIBRARY} ${LIBEVENT_LIBRARY})
add_test(NAME ct_connection COMMAND "$<TARGET_FILE:ct_connection>")
set_tests_properties(ct_connection PROPERTIES LABELS "CT")

add_executable(ct_pipeline ct_pipeline.c)
target_link_libraries(ct_pipeline hiredis_cluster ${SSL_LIBRARY} ${LIBEVENT_LIBRARY})
add_test(NAME ct_pipeline COMMAND "$<TARGET_FILE:ct_pipeline>")
set_tests_properties(ct_pipeline PROPERTIES LABELS "CT")

add_executable(ct_connection_ipv6 ct_connection_ipv6.c)
target_link_libraries(ct_connection_ipv6 hiredis_cluster ${SSL_LIBRARY} ${LIBEVENT_LIBRARY})
add_test(NAME ct_connection_ipv6 COMMAND "$<TARGET_FILE:ct_connection_ipv6>")
set_tests_properties(ct_connection_ipv6 PROPERTIES LABELS "CT")
if(NOT ENABLE_IPV6_TESTS)
  set_tests_properties(ct_connection_ipv6 PROPERTIES DISABLED True)
endif()

add_executable(ct_out_of_memory_handling ct_out_of_memory_handling.c)
target_link_libraries(ct_out_of_memory_handling hiredis_cluster ${SSL_LIBRARY} ${LIBEVENT_LIBRARY})
add_test(NAME ct_out_of_memory_handling COMMAND "$<TARGET_FILE:ct_out_of_memory_handling>")
set_tests_properties(ct_out_of_memory_handling PROPERTIES LABELS "CT")

add_executable(ct_specific_nodes ct_specific_nodes.c test_utils.c)
target_link_libraries(ct_specific_nodes hiredis_cluster ${SSL_LIBRARY} ${LIBEVENT_LIBRARY})
add_test(NAME ct_specific_nodes COMMAND "$<TARGET_FILE:ct_specific_nodes>")
set_tests_properties(ct_specific_nodes PROPERTIES LABELS "CT")

add_executable(ut_parse_cmd ut_parse_cmd.c test_utils.c)
target_link_libraries(ut_parse_cmd hiredis_cluster ${SSL_LIBRARY})
add_test(NAME ut_parse_cmd COMMAND "$<TARGET_FILE:ut_parse_cmd>")
set_tests_properties(ut_parse_cmd PROPERTIES LABELS "UT")

if(ENABLE_SSL)
  # Executable: tls
  add_executable(example_tls main_tls.c)
  target_link_libraries(example_tls hiredis_cluster ${SSL_LIBRARY})
  add_dependencies(example_tls generate_tls_configs)

  # Executable: async tls
  add_executable(example_async_tls main_async_tls.c)
  target_link_libraries(example_async_tls hiredis_cluster ${SSL_LIBRARY} ${LIBEVENT_LIBRARY})
  add_dependencies(example_async_tls generate_tls_configs)
endif()

if(LIBUV_LIBRARY)
  add_executable(ct_async_libuv ct_async_libuv.c)
  target_link_libraries(ct_async_libuv hiredis_cluster ${SSL_LIBRARY} ${LIBUV_LIBRARY})
  add_test(NAME ct_async_libuv COMMAND "$<TARGET_FILE:ct_async_libuv>")
  set_tests_properties(ct_async_libuv PROPERTIES LABELS "CT")
else()
  add_test(NAME ct_async_libuv COMMAND "")
  set_tests_properties(ct_async_libuv PROPERTIES DISABLED True)
endif()

if(LIBEV_LIBRARY)
  add_executable(ct_async_libev ct_async_libev.c)
  # Temporary remove warning of unused parameter due to an issue in hiredis libev adapter
  target_compile_options(ct_async_libev PRIVATE -Wno-unused-parameter)
  target_link_libraries(ct_async_libev hiredis_cluster ${SSL_LIBRARY} ${LIBEV_LIBRARY})
  add_test(NAME ct_async_libev COMMAND "$<TARGET_FILE:ct_async_libev>")
  set_tests_properties(ct_async_libev PROPERTIES LABELS "CT")
else()
  add_test(NAME ct_async_libev COMMAND "")
  set_tests_properties(ct_async_libev PROPERTIES DISABLED True)
endif()

if(GLIB_LIBRARY_FOUND)
  add_executable(ct_async_glib ct_async_glib.c)
  target_link_libraries(ct_async_glib hiredis_cluster ${SSL_LIBRARY} PkgConfig::GLIB_LIBRARY)
  add_test(NAME ct_async_glib COMMAND "$<TARGET_FILE:ct_async_glib>")
  set_tests_properties(ct_async_glib PROPERTIES LABELS "CT")
else()
  add_test(NAME ct_async_glib COMMAND "")
  set_tests_properties(ct_async_glib PROPERTIES DISABLED True)
endif()

# Tests using simulated redis node
add_executable(clusterclient clusterclient.c)
target_link_libraries(clusterclient hiredis_cluster ${SSL_LIBRARY})
add_executable(clusterclient_async clusterclient_async.c)
target_link_libraries(clusterclient_async hiredis_cluster ${SSL_LIBRARY} ${LIBEVENT_LIBRARY})
add_executable(clusterclient_reconnect_async clusterclient_reconnect_async.c)
target_link_libraries(clusterclient_reconnect_async hiredis_cluster ${SSL_LIBRARY} ${LIBEVENT_LIBRARY})
add_test(NAME set-get-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/set-get-test.sh"
                 "$<TARGET_FILE:clusterclient>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME set-get-test-async
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/set-get-test.sh"
         "$<TARGET_FILE:clusterclient_async>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME ask-redirect-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/ask-redirect-test.sh"
         "$<TARGET_FILE:clusterclient>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME ask-redirect-test-async
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/ask-redirect-test.sh"
                 "$<TARGET_FILE:clusterclient_async>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME ask-redirect-using-cluster-nodes-test-async
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/ask-redirect-using-cluster-nodes-test.sh"
                 "$<TARGET_FILE:clusterclient_async>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME moved-redirect-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/moved-redirect-test.sh"
                 "$<TARGET_FILE:clusterclient>"
                 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME moved-redirect-test-async
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/moved-redirect-test.sh"
         "$<TARGET_FILE:clusterclient_async>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME moved-redirect-using-cluster-nodes-test-async
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/moved-redirect-using-cluster-nodes-test.sh"
                 "$<TARGET_FILE:clusterclient_async>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME dbsize-to-all-nodes-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/dbsize-to-all-nodes-test.sh"
                 "$<TARGET_FILE:clusterclient>"
                 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME dbsize-to-all-nodes-test-async
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/dbsize-to-all-nodes-test.sh"
                 "$<TARGET_FILE:clusterclient_async>"
                 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME dbsize-to-all-nodes-during-scaledown-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/dbsize-to-all-nodes-during-scaledown-test.sh"
                 "$<TARGET_FILE:clusterclient>"
                 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME dbsize-to-all-nodes-during-scaledown-test-async
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/dbsize-to-all-nodes-during-scaledown-test-async.sh"
                 "$<TARGET_FILE:clusterclient_async>"
                 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME reconnect-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/reconnect-test.sh"
                "$<TARGET_FILE:clusterclient_reconnect_async>"
                WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME timeout-handling-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/timeout-handling-test.sh"
                 "$<TARGET_FILE:clusterclient_async>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME connect-error-using-cluster-nodes-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/connect-error-using-cluster-nodes-test.sh"
                 "$<TARGET_FILE:clusterclient>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME command-from-callback-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/command-from-callback-test.sh"
                 "$<TARGET_FILE:clusterclient_async>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME ask-redirect-connection-error-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/ask-redirect-connection-error-test.sh"
                 "$<TARGET_FILE:clusterclient_async>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME cluster-down-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/cluster-down-test.sh"
                 "$<TARGET_FILE:clusterclient_async>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME connection-error-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/connection-error-test.sh"
                 "$<TARGET_FILE:clusterclient_async>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME redirect-with-hostname-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/redirect-with-hostname-test.sh"
                 "$<TARGET_FILE:clusterclient>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME redirect-with-ipv6-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/redirect-with-ipv6-test.sh"
                 "$<TARGET_FILE:clusterclient>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME redirect-with-ipv6-async-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/redirect-with-ipv6-test.sh"
                 "$<TARGET_FILE:clusterclient_async>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
if(NOT ENABLE_IPV6_TESTS)
  set_tests_properties(redirect-with-ipv6-test PROPERTIES DISABLED True)
  set_tests_properties(redirect-with-ipv6-async-test PROPERTIES DISABLED True)
endif()
# This test can't be run on hiredis v1.1.0 due to hiredis issue #1171.
# Disabling the testcase if hiredis contains the issue or if the version is unknown.
add_test(NAME redirect-with-hostname-test-async
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/redirect-with-hostname-test.sh"
                 "$<TARGET_FILE:clusterclient_async>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
if(hiredis_VERSION VERSION_EQUAL "1.1.0" OR hiredis_VERSION VERSION_EQUAL "0")
  set_tests_properties(redirect-with-hostname-test-async PROPERTIES DISABLED True)
endif()
add_test(NAME cluster-scale-down-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/cluster-scale-down-test.sh"
                "$<TARGET_FILE:clusterclient>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME slots-not-served-test
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/slots-not-served-test.sh"
                 "$<TARGET_FILE:clusterclient>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
add_test(NAME slots-not-served-test-async
         COMMAND "${CMAKE_SOURCE_DIR}/tests/scripts/slots-not-served-test-async.sh"
                 "$<TARGET_FILE:clusterclient_async>"
         WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/tests/scripts/")
